package com.example.hsh.homefragment.view;

import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.support.v4.view.NestedScrollingChild;
import android.support.v4.view.NestedScrollingChildHelper;
import android.support.v4.view.NestedScrollingParent;
import android.support.v4.view.NestedScrollingParentHelper;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.widget.LinearLayout;
import android.widget.OverScroller;

import com.example.hsh.homefragment.R;


public class StickyNavLayout_ extends LinearLayout implements NestedScrollingParent,NestedScrollingChild{
    private static final String TAG = "StickyNavLayout";
    private float mTotalUnconsumed;
    private NestedScrollingParentHelper mNestedScrollingParentHelper;
    private NestedScrollingChildHelper mNestedScrollingChildHelper;
    private final int[] mParentScrollConsumed = new int[2];
    private final int[] mParentOffsetInWindow = new int[2];
    private ValueAnimator animator;

    @Override
    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
        Log.e(TAG, "onStartNestedScroll");
        return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
    }

    @Override
    public void onNestedScrollAccepted(View child, View target, int axes) {
        Log.e(TAG, "onNestedScrollAccepted");
        // Reset the counter of how much leftover scroll needs to be consumed.
        mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, axes);
        // Dispatch up to the nested parent
        boolean scroll = startNestedScroll(axes & ViewCompat.SCROLL_AXIS_VERTICAL);

        Log.d(TAG, "onNestedScrollAccepted: scroll=" + scroll);

        mTotalUnconsumed = 0;
    }

    @Override
    public void onStopNestedScroll(View target) {
        Log.e(TAG, "onStopNestedScroll");
        mNestedScrollingParentHelper.onStopNestedScroll(target);
        // Finish the spinner for nested scrolling if we ever consumed any
        // unconsumed nested scroll
        if (mTotalUnconsumed > 0) {
            mTotalUnconsumed = 0;
        }
        // Dispatch up our nested parent
        stopNestedScroll();
    }

    @Override
    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
//        Log.e(TAG, "onNestedScroll");

        //child已经不能在向下滑动，判断当前还需要消耗dy，不需要再交给parent
        if (dyUnconsumed < 0 && getScrollY() <= remainHeight && !ViewCompat.canScrollVertically(target, -1)) {
            dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
                    mParentOffsetInWindow);
        Log.d(TAG, "onNestedScroll: dyUnconsumed="+dyUnconsumed);
        }


        //向上滑，当前无需消耗dy，则交给parent，用来隐藏加载
        if (dyConsumed > 0 && getScrollY() >= 0) {
            Log.d(TAG, "onNestedScroll: dyConsumed="+dyConsumed);

            dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
                    mParentOffsetInWindow);
        }

//        Log.d(TAG, "onNestedScroll: dispatchNestedScroll=" + dispatchNestedScroll);
//        Log.d(TAG, "onNestedScroll: mParentOffsetInWindow[1]=" + mParentOffsetInWindow[1]);

        // This is a bit of a hack. Nested scrolling works from the bottom up, and as we are
        // sometimes between two nested scrolling views, we need a way to be able to know when any
        // nested scrolling parent has stopped handling events. We do that by using the
        // 'offset in window 'functionality to see if we have been moved from the event.
        // This is a decent indication of whether we should take over the event stream or not.
        final int dy = dyUnconsumed + mParentOffsetInWindow[1];
        if (dy < 0) {
            mTotalUnconsumed += Math.abs(dy);
        }
    }

    @Override
    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
        Log.e(TAG, "onNestedPreScroll");

        //返回时应该先让parent先消耗
        // Now let our nested parent consume the leftovers
        final int[] parentConsumed = mParentScrollConsumed;
        if (dispatchNestedPreScroll(dx - consumed[0], dy - consumed[1], parentConsumed, null)) {
            Log.d(TAG, "onNestedPreScroll: mParentScrollConsumed=" + parentConsumed[1]);
            consumed[1] = dy;
            dx -= parentConsumed[0];
            dy -= parentConsumed[1];
        }


        boolean hiddenTop = dy > 0 && getScrollY() < mTopViewHeight;
        boolean showTop = dy < 0 && getScrollY() > remainHeight && !ViewCompat.canScrollVertically(target, -1);
        Log.d(TAG, "onNestedPreScroll: dy=" + dy);
        if (hiddenTop || showTop) {
//            scrollBy(0, dy);
            setTopAndBottomOffset(-dy);
            consumed[1] = dy;

            Log.d(TAG, "onNestedPreScroll: consumed[1]=" + consumed[1]);
            Log.d(TAG, "onNestedPreScroll: mTotalUnconsumed=" + mTotalUnconsumed);
        }

        // If we are in the middle of consuming, a scroll, then we want to move the spinner back up
        // before allowing the list to scroll
//        if (dy > 0 && mTotalUnconsumed > 0) {
//            if (dy > mTotalUnconsumed) {
//                consumed[1] = dy - (int) mTotalUnconsumed;
//                mTotalUnconsumed = 0;
//            } else {
//                mTotalUnconsumed -= dy;
//                consumed[1] = dy;
//            }
//        }

    }

    @Override
    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
        Log.e(TAG, "onNestedFling");
        return dispatchNestedFling(velocityX, velocityY, consumed);
    }

    @Override
    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
        Log.e(TAG, "onNestedPreFling");
        //down - //up+
        if (getScrollY() >= mTopViewHeight) return false;

        if (getScrollY() <= remainHeight) {
            return false;
        }

        fling((int) velocityY);
        return dispatchNestedPreFling(velocityX, velocityY);
    }

    @Override
    public int getNestedScrollAxes() {
        Log.e(TAG, "getNestedScrollAxes");
        return mNestedScrollingParentHelper.getNestedScrollAxes();
    }

    private View mTop;
    private View mNav;
    private ViewPager mViewPager;

    private int mTopViewHeight;

    private OverScroller mScroller;

    private int remainHeight;

    private int remainHeaderHeight;

    private boolean firstLayout = true;

    public StickyNavLayout_(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        setOrientation(LinearLayout.VERTICAL);
        mScroller = new OverScroller(context);

        mNestedScrollingChildHelper = new NestedScrollingChildHelper(this);
        mNestedScrollingParentHelper = new NestedScrollingParentHelper(this);
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.StickyNavLayout);
        int indexCount = typedArray.getIndexCount();
        for (int i=0;i<indexCount;i++) {
            int index = typedArray.getIndex(i);
            switch (index) {
                case R.styleable.StickyNavLayout_remainHeight:
                    remainHeaderHeight = typedArray.getDimensionPixelSize(index, 0);
                    break;
            }
        }
        typedArray.recycle();
        setNestedScrollingEnabled(true);


        remainHeight = remainHeaderHeight;
        mViewOffsetHelper = new ViewOffsetHelper(this);

        mViewOffsetHelper.setTopAndBottomOffset(-remainHeaderHeight);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mTop = findViewById(R.id.top_panel);
        mNav = findViewById(R.id.tl_tabs);
        View view = findViewById(R.id.vp_main);
        if (!(view instanceof ViewPager)) {
            throw new RuntimeException(
                    "id_stickynavlayout_viewpager show used by ViewPager !");
        }
        mViewPager = (ViewPager) view;
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        mViewOffsetHelper.onViewLayout(0, mTempTopBottomOffset);

        if (mTempTopBottomOffset != 0) {
            mViewOffsetHelper.setTopAndBottomOffset(mTempTopBottomOffset);
            mTempTopBottomOffset = 0;
        }
        if (mTempLeftRightOffset != 0) {
            mViewOffsetHelper.setLeftAndRightOffset(mTempLeftRightOffset);
            mTempLeftRightOffset = 0;
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //不限制顶部的高度
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//        getChildAt(0).measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.EXACTLY));
        measureChild(mTop,widthMeasureSpec,heightMeasureSpec);
        //设置ViewPager的高度，
        ViewGroup.LayoutParams params = mViewPager.getLayoutParams();
        params.height = getMeasuredHeight() - mNav.getMeasuredHeight();

        setMeasuredDimension(getMeasuredWidth(), mTop.getMeasuredHeight() + mNav.getMeasuredHeight() + mViewPager.getMeasuredHeight());
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mTopViewHeight = mTop.getMeasuredHeight();
        Log.d(TAG, "onSizeChanged: mTopViewHeight=" + mTopViewHeight);
    }

    public void openRemainHeader(boolean anim){
        if (!anim) {
//            super.scrollTo(0, 0);
            scrollBy(0, (int) getResources().getDimension(R.dimen.test_dimen));
            remainHeight = 0;
//            requestFocus();
        }else{
            animateRemainHeader(getScrollY(), 0);
        }
    }

    public void closeRemainHeader(boolean anim){
        if (!anim) {
            super.scrollTo(0, remainHeaderHeight);
            remainHeight = remainHeaderHeight;
            requestLayout();
        }else{
            animateRemainHeader(getScrollY(), remainHeaderHeight);
        }
    }

    private void animateRemainHeader(int start, int end) {
        Log.d(TAG, "animateRemainHeader: start=" + start + ",end=" + end);

        if (animator != null && animator.isRunning()) {
            return;
        }

        if (animator == null) {
            animator = new ValueAnimator();
            animator.setInterpolator(new DecelerateInterpolator());
            animator.addUpdateListener(animation -> {
                int currentY = (int) animation.getAnimatedValue();
                Log.d(TAG, "animateRemainHeader: currentY=" + currentY);
                Log.d(TAG, "animateRemainHeader: currentY - getScrollY()=" + (currentY - getScrollY()));
//                requestFocus();
//                scrollBy(0, (int) getResources().getDimension(R.dimen.test_dimen));

//                setHeaderTopBottomOffset(currentY, 0, mTopViewHeight);
//                offsetTopAndBottom(currentY);
            });
        }else{
            animator.cancel();
        }
        animator.setDuration(600);
        animator.setIntValues(start, end);
        animator.start();
    }


    private int setHeaderTopBottomOffset(int newOffset,
                                 int minOffset, int maxOffset) {
        final int curOffset = getTopAndBottomOffset();
        int consumed = 0;
        Log.d(TAG, "setHeaderTopBottomOffset: curOffset=" + curOffset);
        if (minOffset != 0 && curOffset >= minOffset && curOffset <= maxOffset) {
            // If we have some scrolling range, and we're currently within the min and max
            // offsets, calculate a new offset
            newOffset = constrain(newOffset, minOffset, maxOffset);

            if (curOffset != newOffset) {
                setTopAndBottomOffset(newOffset);
                // Update how much dy we have consumed
                consumed = curOffset - newOffset;
            }
        }

        return consumed;
    }

    public void fling(int velocityY) {
        mScroller.fling(0, getScrollY(), 0, velocityY, 0, 0, 0, mTopViewHeight - remainHeight);
        invalidate();
    }

    @Override
    public void scrollTo(int x, int y) {
        if (y < 0) {
            y = 0;
        }
        if (y > mTopViewHeight) {
            y = mTopViewHeight;
        }

        if (y < remainHeight) {
            y = remainHeight;
        }

        if (y != getScrollY()) {
            super.scrollTo(x, y);
        }
    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            scrollTo(0, mScroller.getCurrY());
            invalidate();
        }
    }

    public void setNestedScrollingEnabled(boolean enabled) {
        mNestedScrollingChildHelper.setNestedScrollingEnabled(enabled);
    }

    public boolean isNestedScrollingEnabled() {
        return mNestedScrollingChildHelper.isNestedScrollingEnabled();
    }
    public boolean startNestedScroll(int axes) {
        return mNestedScrollingChildHelper.startNestedScroll(axes);
    }
    public void stopNestedScroll() {
        mNestedScrollingChildHelper.stopNestedScroll();
    }
    public boolean hasNestedScrollingParent() {
        return mNestedScrollingChildHelper.hasNestedScrollingParent();
    }
    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
                                        int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
        return mNestedScrollingChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed,
                dxUnconsumed, dyUnconsumed, offsetInWindow);
    }
    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
        return mNestedScrollingChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
    }
    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
        return mNestedScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
    }
    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
        return mNestedScrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY);
    }


    int constrain(int amount, int low, int high) {
        return amount < low ? low : (amount > high ? high : amount);
    }

    float constrain(float amount, float low, float high) {
        return amount < low ? low : (amount > high ? high : amount);
    }


    private ViewOffsetHelper mViewOffsetHelper;

    private int mTempTopBottomOffset = 0;
    private int mTempLeftRightOffset = 0;

    public boolean setTopAndBottomOffset(int offset) {
        if (mViewOffsetHelper != null) {
            return mViewOffsetHelper.setTopAndBottomOffset(offset);
        } else {
            mTempTopBottomOffset = offset;
        }
        return false;
    }

    public boolean setLeftAndRightOffset(int offset) {
        if (mViewOffsetHelper != null) {
            return mViewOffsetHelper.setLeftAndRightOffset(offset);
        } else {
            mTempLeftRightOffset = offset;
        }
        return false;
    }

    public int getTopAndBottomOffset() {
        return mViewOffsetHelper != null ? mViewOffsetHelper.getTopAndBottomOffset() : 0;
    }

    public int getLeftAndRightOffset() {
        return mViewOffsetHelper != null ? mViewOffsetHelper.getLeftAndRightOffset() : 0;
    }

    class ViewOffsetHelper {

        private final View mView;

        private int mLastOffsetTop;
        private int mLastOffsetLeft;
        private int mOffsetTop;
        private int mOffsetLeft;

        public ViewOffsetHelper(View view) {
            mView = view;
        }

        public void onViewLayout(int offsetLeft,int offsetTop) {
            // Now grab the intended top
            mLastOffsetTop = offsetTop;
            mLastOffsetLeft = offsetLeft;
            Log.d(TAG, "onViewLayout: mLayoutTop=" + offsetTop);
            // And offset it as needed
            updateOffsets();
        }

        private void updateOffsets() {
            ViewCompat.offsetTopAndBottom(mView, mOffsetTop - mLastOffsetTop);
            ViewCompat.offsetLeftAndRight(mView, mOffsetLeft - mLastOffsetLeft);
            mLastOffsetTop = mOffsetTop;
            mLastOffsetLeft = mOffsetLeft;
        }

        /**
         * Set the top and bottom offset for this {@link android.support.design.widget.ViewOffsetHelper}'s view.
         *
         * @param offset the offset in px.
         * @return true if the offset has changed
         */
        public boolean setTopAndBottomOffset(int offset) {
            if (mOffsetTop != offset) {
                mOffsetTop = offset;
                updateOffsets();
                return true;
            }
            return false;
        }

        /**
         * Set the left and right offset for this {@link android.support.design.widget.ViewOffsetHelper}'s view.
         *
         * @param offset the offset in px.
         * @return true if the offset has changed
         */
        public boolean setLeftAndRightOffset(int offset) {
            if (mOffsetLeft != offset) {
                mOffsetLeft = offset;
                updateOffsets();
                return true;
            }
            return false;
        }

        public int getTopAndBottomOffset() {
            return mOffsetTop;
        }

        public int getLeftAndRightOffset() {
            return mOffsetLeft;
        }

        public int getLastOffsetTop() {
            return mLastOffsetTop;
        }

        public int getLastOffsetLeft() {
            return mLastOffsetLeft;
        }
    }
}